#include "pointaxis.h"
//#include <dtCore/collisioncategorydefaults.h>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/Switch>
#include <osg/MatrixTransform>
#include <osgText/Text>
#include <cassert>

namespace CoreBase
{
	const float                PointAxis::DEF_AXIS_SIZE(1.0f);
	const float                PointAxis::DEF_CHAR_SIZE(0.3f);
	const PointAxis::AXISCOLOR PointAxis::DEF_COLOR[NUMAXES] =
	{
		RED,
		GREEN,
		BLUE
	};

	const char* PointAxis::DEF_LABEL_XYZ[NUMAXES] =
	{
		"X",
		"Y",
		"Z"
	};

	const char* PointAxis::DEF_LABEL_HPR[NUMAXES] =
	{
		"P",
		"R",
		"H"
	};

	const osg::Vec4 PointAxis::COLOR[BASECOLORS] =
	{
		osg::Vec4(0.0f, 0.0f, 0.0f, 1.0f),
		osg::Vec4(1.0f, 1.0f, 1.0f, 1.0f),
		osg::Vec4(1.0f, 0.0f, 0.0f, 1.0f),
		osg::Vec4(1.0f, 1.0f, 0.0f, 1.0f),
		osg::Vec4(0.0f, 1.0f, 0.0f, 1.0f),
		osg::Vec4(0.0f, 1.0f, 1.0f, 1.0f),
		osg::Vec4(0.0f, 0.0f, 1.0f, 1.0f),
		osg::Vec4(1.0f, 0.0f, 1.0f, 1.0f),
	};

	PointAxis::PointAxis():Transformable(NULL), _mainSwitch(NULL)
		, _labelSwitch(NULL) , _characterScale(1.0f)
	{
		memset(_point, 0L, sizeof(_point));
		for(int ii(0L); ii < NUMAXES; ii++)
		{
			_label[ii]        = DEF_LABEL_XYZ[ii];
			_cLabel[ii]       = DEF_LABEL_XYZ[ii];
			_colorID[ii]      = DEF_COLOR[ii];
			_lColorID[ii]     = DEF_COLOR[ii];
			_color[ii]        = COLOR[DEF_COLOR[ii]];
			_lColor[ii]       = COLOR[DEF_COLOR[ii]];
			_point[ii][ii]    = DEF_AXIS_SIZE;
		}
		ctor();
		setName("PointAxis");
		//SetCollisionCategoryBits(COLLISION_CATEGORY_MASK_POINTAXIS);
	}

	PointAxis::~PointAxis()
	{}

	void PointAxis::enable(int f)
	{
		if(f & AXES)
		{
			_mainSwitch->setValue(1L, true);
		}
		if(f & LABELS)
		{
			_mainSwitch->setValue(0L, true);
		}
		if(f & LABEL_X)
		{
			_labelSwitch->setValue(X, true);
		}
		if(f & LABEL_Y)
		{
			_labelSwitch->setValue(Y, true);
		}
		if(f & LABEL_Z)
		{
			_labelSwitch->setValue(Z, true);
		}
		if(f & XYZ_LABELS)
		{
			osg::Geode* g(NULL);
			g = static_cast<osg::Geode*>(_labelSwitch->getChild(X));
			assert(g);
			_label[X] = DEF_LABEL_XYZ[X];
			labelSetup(g, _label[X].toStdString().c_str(), _point[X], _lColor[X], getLength(X));
			g = static_cast<osg::Geode*>(_labelSwitch->getChild(Y));
			assert(g);
			_label[Y] = DEF_LABEL_XYZ[Y];
			labelSetup(g, _label[Y].toStdString().c_str(), _point[Y], _lColor[Y], getLength(Y));
			g = static_cast<osg::Geode*>(_labelSwitch->getChild(Z));
			assert(g);
			_label[Z] = DEF_LABEL_XYZ[Z];
			labelSetup(g, _label[Z].toStdString().c_str(), _point[Z], _lColor[Z], getLength(Z));
		}
		if(f & HPR_LABELS)
		{
			osg::Geode* g(NULL);
			g = static_cast<osg::Geode*>(_labelSwitch->getChild(X));
			assert(g);
			_label[X] = DEF_LABEL_HPR[X];
			labelSetup(g, _label[X].toStdString().c_str(), _point[X], _lColor[X], getLength(X));
			g = static_cast<osg::Geode*>(_labelSwitch->getChild(Y));
			assert(g);
			_label[Y] = DEF_LABEL_HPR[Y];
			labelSetup(g, _label[Y].toStdString().c_str(), _point[Y], _lColor[Y], getLength(Y));
			g = static_cast<osg::Geode*>(_labelSwitch->getChild(Z));
			assert(g);
			_label[Z] = DEF_LABEL_HPR[Z];
			labelSetup(g, _label[Z].toStdString().c_str(), _point[Z], _lColor[Z], getLength(Z));
		}
		if(f & CUSTOM_LABELS)
		{
			osg::Geode* g(NULL);
			g = static_cast<osg::Geode*>(_labelSwitch->getChild(X));
			assert(g);
			_label[X] = _cLabel[X];
			labelSetup(g, _label[X].toStdString().c_str(), _point[X], _lColor[X], getLength(X));
			g = static_cast<osg::Geode*>(_labelSwitch->getChild(Y));
			assert(g);
			_label[Y] = _cLabel[Y];
			labelSetup(g, _label[Y].toStdString().c_str(), _point[Y], _lColor[Y], getLength(Y));
			g = static_cast<osg::Geode*>(_labelSwitch->getChild(Z));
			assert(g);
			_label[Z] = _cLabel[Z];
			labelSetup(g, _label[Z].toStdString().c_str(), _point[Z], _lColor[Z], getLength(Z));
		}
	}

	void PointAxis::disable(int f)
	{
		if(f & AXES)
		{
			_mainSwitch->setValue(1L, false);
		}
		if(f & LABELS)
		{
			_mainSwitch->setValue(0L, false);
		}
		if(f & LABEL_X)
		{
			_labelSwitch->setValue(X, false);
		}
		if(f & LABEL_Y)
		{
			_labelSwitch->setValue(Y, false);
		}
		if(f & LABEL_Z)
		{
			_labelSwitch->setValue(Z, false);
		}
	}

	const char* PointAxis::getLabel(AXIS a) const
	{
		if(a >= NUMAXES)
		{
			return NULL;
		}
		return _label[a].toStdString().c_str();
	}

	void PointAxis::setLabel(AXIS a, const char* l)
	{
		if(a >= NUMAXES)
		{
			return;
		}
		_cLabel[a] = l;
		enable(CUSTOM_LABELS);
	}

	float PointAxis::getLength(AXIS a) const
	{
		if(a >= NUMAXES)
		{
			return 0.0f;
		}
		return _point[a][a];
	}

	void PointAxis::setLength(AXIS a, float l)
	{
		if(a >= NUMAXES)
		{
			return;
		}
		_point[a][a] = l;
		axesSetup();
		osg::Geode* g = static_cast<osg::Geode*>(_labelSwitch->getChild(a));
		assert(g);
		labelSetup(g, _label[a].toStdString().c_str(), _point[a], _lColor[a], l);
	}

	PointAxis::AXISCOLOR PointAxis::getColor(AXIS a) const
	{
		if(a >= NUMAXES)
		{
			return NUMAXISCOLORS;
		}
		return _colorID[a];
	}

	void PointAxis::setColor(AXIS a, AXISCOLOR c)
	{
		if((a >= NUMAXES) || (int(c) >= int(BASECOLORS)))
		{
			return;
		}
		_colorID[a] = c;
		_color[a] = COLOR[_colorID[a]];
		axesSetup();
	}

	void PointAxis::getColor(AXIS a, osg::Vec4& c) const
	{
		if(a >= NUMAXES)
		{
			return;
		}
		c = _color[a];
	}

	void PointAxis::setColor(AXIS a, const osg::Vec4 c)
	{
		if(a >= NUMAXES)
		{
			return;
		}
		_colorID[a] = AXISCOLOR(a + BASECOLORS);
		_color[a]   = c;
		axesSetup();
	}

	PointAxis::AXISCOLOR PointAxis::getLabelColor(AXIS a) const
	{
		if(a >= NUMAXES)
		{
			return NUMAXISCOLORS;
		}
		return _lColorID[a];
	}

	void PointAxis::setLabelColor(AXIS a, AXISCOLOR c)
	{
		if((a >= NUMAXES) || (int(c) >= int(BASECOLORS)))
		{
			return;
		}
		_lColorID[a] = c;
		_lColor[a]   = COLOR[_lColorID[a]];
		osg::Geode* g = static_cast<osg::Geode*>(_labelSwitch->getChild(a));
		assert(g);
		labelSetup(g, _label[a].toStdString().c_str(), _point[a], _lColor[a], getLength(a));
	}

	void PointAxis::getLabelColor(AXIS a, osg::Vec4& c) const
	{
		if(a >= NUMAXES)
		{
			return;
		}
		c = _lColor[a];
	}

	void PointAxis::setCharacterScale(float size)
	{
		_characterScale = size;
	}

	float PointAxis::getCharacterScale() const
	{
		return _characterScale;
	}

	void PointAxis::setLabelColor(AXIS a, const osg::Vec4 c)
	{
		if(a >= NUMAXES)
		{
			return;
		}
		_lColorID[a] = AXISCOLOR(a + BASECOLORS);
		_lColor[a]   = c;
		osg::Geode* g = static_cast<osg::Geode*>(_labelSwitch->getChild(a));
		assert(g);
		labelSetup(g, _label[a].toStdString().c_str(), _point[a], _lColor[a], getLength(a));
	}

	void PointAxis::ctor(void)
	{
		osg::Geode* geode(NULL);
		osg::StateSet* ss(NULL);
		_mainSwitch = new osg::Switch;
		assert(_mainSwitch.get());
		getMatrixNode()->addChild(_mainSwitch.get());
		_labelSwitch = new osg::Switch;
		assert(_labelSwitch.get());
		_mainSwitch->addChild(_labelSwitch.get(), true);
		axesSetup();
		geode = new osg::Geode;
		assert(geode);
		ss = geode->getOrCreateStateSet();
		assert(ss);
		ss->setRenderBinDetails(20, "RenderBin");
		ss->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
		ss->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
		labelSetup(geode, _label[X].toStdString().c_str(), _point[X], _lColor[X], getLength(X));
		_labelSwitch->addChild(geode, false);
		// create the Y-label
		geode = new osg::Geode;
		assert(geode);
		ss = geode->getOrCreateStateSet();
		assert(ss);
		ss->setRenderBinDetails(20, "RenderBin");
		ss->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
		ss->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
		labelSetup(geode, _label[Y].toStdString().c_str(), _point[Y], _lColor[Y], getLength(Y));
		_labelSwitch->addChild(geode, false);
		// create the Z-label
		geode = new osg::Geode;
		assert(geode);
		ss = geode->getOrCreateStateSet();
		assert(ss);
		ss->setRenderBinDetails(20, "RenderBin");
		ss->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
		ss->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
		labelSetup(geode, _label[Z].toStdString().c_str(), _point[Z], _lColor[Z], getLength(Z));
		_labelSwitch->addChild(geode, false);
	}

	void PointAxis::axesSetup(void)
	{
		assert(_mainSwitch.get());
		osg::Geode*    geode(NULL);
		osg::Geometry* geome(NULL);
		if(_mainSwitch->getNumChildren() < 2L)
		{
			geode = new osg::Geode;
			assert(geode);
			geome = new osg::Geometry;
			assert(geome);
			geode->addDrawable(geome);
			_mainSwitch->addChild(geode, true);
			osg::StateSet* ss = geode->getOrCreateStateSet();
			assert(ss);
			ss->setRenderBinDetails(20L, "RenderBin");
			ss->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
			ss->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
		}
		else
		{
			geode = static_cast<osg::Geode*>(_mainSwitch->getChild(1L));
			assert(geode);
			geome = static_cast<osg::Geometry*>(geode->getDrawable(0L));
			assert(geome);
		}
		osg::Vec3 vertices[6] =
		{
			_point[NUMAXES],  // origin
			_point[X],        // X-point
			_point[NUMAXES],  // origin
			_point[Y],        // Y-point
			_point[NUMAXES],  // origin
			_point[Z],        // Y-point
		};
		geome->setVertexArray(new osg::Vec3Array(6, vertices));
		geome->setColorArray(new osg::Vec4Array(3, _color));
		geome->setColorBinding(osg::Geometry::BIND_PER_PRIMITIVE);
		if(geome->getPrimitiveSetList().empty())
		{
			geome->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINES, 0, 6));
		}
		else
		{
			assert(geome->getPrimitiveSetList().size() == 1);
			geome->setPrimitiveSet(0, new osg::DrawArrays(osg::PrimitiveSet::LINES, 0, 6));
		}
	}

	void PointAxis::labelSetup(osg::Geode* g, const char* l, osg::Vec3 p, osg::Vec4 c, float s /*= 1.0f*/)
	{
		assert(g);
		osgText::Text* txt(NULL);
		if(g->getNumDrawables())
		{
			txt = static_cast<osgText::Text*>(g->getDrawable(0L));
			assert(txt);
		}
		else
		{
			txt = new osgText::Text;
			assert(txt);
			g->addDrawable(txt);
		}
		txt->setText(l);
		txt->setCharacterSize(s * DEF_CHAR_SIZE * _characterScale);
		txt->setAutoRotateToScreen(true);
		txt->setColor(c);
		txt->setPosition(p);
	}
}